package logic

import (
	"context"
	"fmt"
	"log"
	"shop-manager/internal/do"
	"shop-manager/internal/repos/person"
	"shop-manager/internal/repos/supermarket"
	"shop-manager/internal/repos/supermarket_director"
	"shop-manager/internal/vo"

	"github.com/samber/lo"
)

//go:generate newc
type SupermarketDirectorLogic struct {
	supermarketDirectorRepo *supermarket_director.SupermarketDirectorRepo
	supermarketRepo         *supermarket.SupermarketRepo
	personRepo              *person.PersonRepo
}

func (l *SupermarketDirectorLogic) CreateSupermarketDirector(ctx context.Context, req vo.CreateSupermarketDirectorReq) (*vo.CreateSupermarketDirectorRsp, error) {
	ss, _, err := l.supermarketRepo.List(ctx, supermarket.ListParam{
		IDs: []int{req.SupermarketID},
	})
	if err != nil {
		return nil, err
	}
	if len(ss) == 0 {
		return nil, fmt.Errorf("supermarket not found, id:%v", req.SupermarketID)
	}

	ps, _, err := l.personRepo.List(ctx, person.ListParam{
		IDs: []int{req.DirectorID},
	})
	if err != nil {
		return nil, err
	}
	if len(ps) == 0 {
		return nil, fmt.Errorf("director not found, id:%v", req.DirectorID)
	}

	id, err := l.supermarketDirectorRepo.Create(ctx, &do.SupermarketDirector{
		DirectorID:    req.DirectorID,
		SupermarketID: req.SupermarketID,
		Note:          req.Note,
	})
	if err != nil {
		return nil, err
	}

	return &vo.CreateSupermarketDirectorRsp{
		ID: id,
	}, nil
}

func (l *SupermarketDirectorLogic) Update(ctx context.Context, req *vo.UpdateSupermarketDirectorReq) (_ *vo.UpdateSupermarketDirectorRsp, err error) {
	ss, _, err := l.supermarketRepo.List(ctx, supermarket.ListParam{
		IDs: []int{req.SupermarketID},
	})
	if err != nil {
		return nil, err
	}
	if len(ss) == 0 {
		return nil, fmt.Errorf("supermarket not found, id:%v", req.SupermarketID)
	}

	ps, _, err := l.personRepo.List(ctx, person.ListParam{
		IDs: []int{req.DirectorID},
	})
	if err != nil {
		return nil, err
	}
	if len(ps) == 0 {
		return nil, fmt.Errorf("director not found, id:%v", req.DirectorID)
	}

	err = l.supermarketDirectorRepo.Update(ctx, &do.SupermarketDirector{
		ID:             req.ID,
		DirectorID:     req.DirectorID,
		SupermarketID:  req.SupermarketID,
		DirectorPoints: req.DirectorPoints,
		Note:           req.Note,
	})
	if err != nil {
		return nil, err
	}

	return &vo.UpdateSupermarketDirectorRsp{}, nil
}

type ListSupermarketDirectorRsp struct {
	Items []*vo.ListSupermarketDirectorRspItem
	Total int64
}

// list
func (l *SupermarketDirectorLogic) ListSupermarketDirector(ctx context.Context, req vo.ListSupermarketDirectorReq) (*ListSupermarketDirectorRsp, error) {
	listData, total, err := l.supermarketDirectorRepo.List(ctx, supermarket_director.ListParam{
		Page:           req.Page,
		PageSize:       req.PageSize,
		Search:         req.Search,
		Sorts:          req.Sorts,
		SupermarketIDs: req.SupermarketIDs,
	})
	if err != nil {
		return nil, err
	}

	supermarketIds := lo.Map(listData, func(item *do.SupermarketDirector, index int) int {
		return item.SupermarketID
	})
	// superMarketId => superMarket
	superMarketMap, err := l.supermarketRepo.GetSupermarketMap(ctx, supermarket.ListParam{
		IDs:      supermarketIds,
		PageSize: -1,
	})
	if err != nil {
		return nil, err
	}

	personIds := lo.Map(listData, func(item *do.SupermarketDirector, index int) int {
		return item.DirectorID
	})
	// personId => person
	personMap, err := l.personRepo.GetPersonMap(ctx, person.ListParam{
		IDs:      personIds,
		PageSize: -1,
	})
	if err != nil {
		return nil, err
	}

	retItems := lo.Map(listData, func(item *do.SupermarketDirector, index int) *vo.ListSupermarketDirectorRspItem {
		var supermarketDo *do.Supermarket
		if superMarket, ok := superMarketMap[item.SupermarketID]; ok {
			supermarketDo = superMarket
		} else {
			log.Printf("supermarket not found, id:%v", item.SupermarketID)
		}

		var personDo *do.Person
		if person, ok := personMap[item.DirectorID]; ok {
			personDo = person
		} else {
			log.Printf("person not found, id:%v", item.DirectorID)
		}

		ret := &vo.ListSupermarketDirectorRspItem{
			ID:   item.ID,
			Note: item.Note,
		}
		if supermarketDo != nil {
			ret.Supermarket = vo.Supermarket{
				ID:   supermarketDo.ID,
				Name: supermarketDo.Name,
			}
		}
		if personDo != nil {
			ret.Director = vo.Person{
				ID:          personDo.ID,
				Name:        personDo.Name,
				Note:        personDo.Note,
				Type:        personDo.Type,
				TypeDisplay: personDo.Type.String(),
			}
		}

		return ret
	})

	return &ListSupermarketDirectorRsp{
		Items: retItems,
		Total: total,
	}, nil
}
